﻿// SerialPortManager.cs
using System.IO.Ports;
using System.Text;
using System.Diagnostics;
#if WINDOWS
using System.Management;
#endif

namespace SerialInput;

public class SerialPortManager : IDisposable
{
    private SerialPort? _serialPort;
    private readonly ILogger<SerialPortManager> _logger;
    private readonly Timer _reconnectTimer;
    private bool _isDisposed = false;
    private readonly List<byte> _buffer = new();
    private readonly object _bufferLock = new();
    public event Action<string>? StatusChanged;
    private readonly IConfiguration _config;
    private readonly Dictionary<string, SerialPort> _serialPorts = new();
    private readonly Dictionary<string, List<byte>> _buffers = new();
    private readonly Dictionary<string, SerialPortConfig> _configs = new();
    public event Action<string, ParsedData>? DataReceived; // 增加串口名称

    public bool IsConnected => _serialPort?.IsOpen == true;

    public SerialPortManager(ILogger<SerialPortManager> logger, IConfiguration? config = null)
    {
        _logger = logger;
        _config = config ?? new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: true).Build();
        _reconnectTimer = new Timer(TryReconnect, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10));
    }

    public bool AutoConnect()
    {
        try
        {
            var connConfigs = _config.GetSection("Conn").Get<List<SerialPortConfig>>() ?? new();
            bool anyConnected = false;
            foreach (var cfg in connConfigs)
            {
                string? portName = cfg.PortName;

                if (string.IsNullOrWhiteSpace(portName))
                {
#if WINDOWS
                    portName = FindPortByDeviceNameWindows(cfg.Name);
#else
                    portName = FindPortByDeviceNameLinux(cfg.Name);
#endif
                }
                if (!string.IsNullOrEmpty(portName) && ConnectToPort(cfg.Name, portName, cfg))
                {
                    StatusChanged?.Invoke($"已连接到 {cfg.Name} ({portName})");
                    anyConnected = true;
                }
                else
                {
                    StatusChanged?.Invoke($"未能连接到 {cfg.Name}");
                }
            }
            return anyConnected;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "自动连接失败");
            return false;
        }
    }

#if WINDOWS
    private string FindPortByDeviceNameWindows(string deviceName)
    {
        try
        {
            using var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Caption LIKE '%COM%'");
            foreach (ManagementObject obj in searcher.Get())
            {
                string caption = obj["Caption"]?.ToString() ?? "";
                if (caption.ToLower().Contains(deviceName.ToLower()))
                {
                    var match = System.Text.RegularExpressions.Regex.Match(caption, @"COM\d+");
                    if (match.Success) return match.Value;
                }
            }
        }
        catch { }
        return "";
    }
#else
    private string FindPortByDeviceNameLinux(string deviceName)
    {
        try
        {
            var ports = Directory.GetFiles("/dev/", "ttyUSB*")
                .Concat(Directory.GetFiles("/dev/", "ttyACM*"))
                .ToList();
            Console.WriteLine("检测到串口设备: " + string.Join(", ", ports));
            return ports.FirstOrDefault(p => p.ToLower().Contains(deviceName.ToLower())) ?? "";
        }
        catch { return ""; }
    }
#endif

    private bool ConnectToPort(string name, string portName, SerialPortConfig cfg)
    {
        try
        {
            // ✅ 先判断是否已经连接并且串口是打开的
            if (_serialPorts.TryGetValue(name, out var existingSp) && existingSp.IsOpen)
            {
                _logger.LogInformation($"串口 {name} ({portName}) 已经连接，无需重新连接");
                return true;
            }
            Disconnect(name);
            Encoding encoding;
            try
            {
                encoding = Encoding.GetEncoding(cfg.Encoding);
            }
            catch
            {
                encoding = Encoding.UTF8;
            }
            if (!SerialPort.GetPortNames().Contains(portName))
            {
                _logger.LogWarning($"串口 {portName} 不存在");
                return false;
            }

            var sp = new SerialPort(portName)
            {
                BaudRate = cfg.BaudRate,
                DataBits = cfg.DataBits,
                Parity = ParseParity(cfg.Parity),
                StopBits = (StopBits)cfg.StopBits,
                Encoding = encoding,
                ReadTimeout = cfg.ReadTimeout,
                WriteTimeout = cfg.WriteTimeout
            };

            sp.DataReceived += (s, e) => OnDataReceived(name, sp);

            try
            {
                sp.Open();
                _serialPorts[name] = sp;
                _buffers[name] = new List<byte>();
                _configs[name] = cfg;
            }
            catch (IOException ioEx)
            {
                _logger.LogError(ioEx, $"串口 {portName} 打开失败，可能被占用或已断开");
                return false;
            }

            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"连接串口 {portName} 失败");
            return false;
        }
    }
    private void OnDataReceived(string name, SerialPort sp)
    {
        lock (_bufferLock)
        {
            int bytesToRead = sp.BytesToRead;
            if (bytesToRead > 0)
            {
                byte[] tempBuffer = new byte[bytesToRead];
                sp.Read(tempBuffer, 0, bytesToRead);
                _buffers[name].AddRange(tempBuffer);

                // 获取当前串口的编码
                string encodingName = _configs.TryGetValue(name, out var cfg) ? cfg.Encoding : "UTF-8";

                if (IsPacketComplete(_buffers[name], encodingName))
                {
                    ProcessCompletePacket(name, _buffers[name].ToArray());
                    _buffers[name].Clear();
                }
            }
        }
    }
    private bool IsPacketComplete(List<byte> buffer, string encodingName)
    {
        if (buffer.Count == 0) return false;

        try
        {
            var encoding = Encoding.GetEncoding(encodingName);
            string dataStr = encoding.GetString(buffer.ToArray());

            // ① 如果包含协议字段 P1:，优先用协议字段判断
            if (dataStr.Contains("P1:"))
            {
                return dataStr.Contains("P31:");
            }

            // ② 如果没有协议字段，则用结尾字符判断
            byte lastByte = buffer[^1];
            if (lastByte == 0x0D || lastByte == 0x0A)
            {
                return true;
            }

            // ③ 两者都没有，则不校验
            return false;
        }
        catch
        {
            return false;
        }
    }

    private void ProcessCompletePacket(string name, byte[] packet)
    {
        if (_configs.TryGetValue(name, out var cfg))
        {
            var parsedData = SerialDataParser.ParseSerialData(packet, cfg.Encoding);
            DataReceived?.Invoke(name, parsedData);
        }
        else
        {
            var parsedData = SerialDataParser.ParseSerialData(packet, "UTF-8");
            DataReceived?.Invoke(name, parsedData);
        }
    }
    public void SendData(string name, byte[] data)
    {
        if (_serialPorts.TryGetValue(name, out var sp) && sp.IsOpen)
        {
            sp.Write(data, 0, data.Length);
        }
        else
        {
            _logger.LogWarning($"串口 {name} 未连接，无法发送数据");
        }
    }

    public void Disconnect(string name)
    {
        if (_serialPorts.TryGetValue(name, out var sp))
        {
            if (sp.IsOpen) sp.Close();
            sp.Dispose();
            _serialPorts.Remove(name);
            _buffers.Remove(name);
        }
    }

    private void TryReconnect(object? state)
    {
        if (!_isDisposed) AutoConnect();
    }

    public void Dispose()
    {
        if (!_isDisposed)
        {
            _isDisposed = true;
            _reconnectTimer.Dispose();
            foreach (var name in _serialPorts.Keys.ToList())
            {
                Disconnect(name);
            }
        }
    }
    private Parity ParseParity(string parity)
    {
        return parity.ToLower() switch
        {
            "none" => Parity.None,
            "odd" => Parity.Odd,
            "even" => Parity.Even,
            _ => Parity.None
        };
    }
}